<?php

/**
 * 
 * @desc 框架系统错误捕捉类
 * @author keluo<pycorvn@yeah.net>
 * @since 2015-01-24
 */

class SCError {
    
    public function __construct() {}
    
    /**
     * 
     * 系统错误处理
     * @param integer $code 错误状态码
     * @param string $message
     * @param string $file
     * @param integer $line
     * @param array $context
     */
    public static function errorHandler($code, $message, $file, $line, $context = null) {
        $trace = self::getDebugTraceString();
        self::showDebugPage(false, compact('code', 'message', 'file', 'line', 'trace'));
        return true;
    }
    
    /**
     * 异常错误处理
     * @param Exception $e
     */
    public static function exceptionHandler(Exception $e) {
        $log = array(
            'code' => $e->getCode(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'message' => $e->getMessage(),
            'trace' =>self::getDebugTraceString()
        );
        self::showDebugPage(true, $log);
    }
    
    /**
     * 致命错误处理
     */
    public static function fatalHandler() {
        if(!is_null($e = error_get_last())) {
            $messages = explode(PHP_EOL, $e['message']);
            $message = array_shift($messages);
            if($messages)  unset($messages[0]);
            $log = array(
               'code' => $e['type'],
               'file' => $e['file'],
               'line' => $e['line'],
               'message' => $message,
               'trace' => implode(PHP_EOL, $messages)
           );
           self::showDebugPage(true, $log);
        }
    }
    
    public static function setError($msg) {
        SCScabish::core('session')->set('SC_EXCEPTION_MESSAGE', $msg);
    }
    
    public static function getError() {
        return SCScabish::core('session')->get('SC_EXCEPTION_MESSAGE');
    }
    
    /**
     * 显示debug页面
     * @param boolean $stop 是否停止执行
     * @param array $data debug信息
     */
    protected static function showDebugPage($stop = true, array $data) {
    	if(SC_DEBUG) {
	        $msg = '<meta http-equiv="content-type" content="text/html;charset=utf-8">';
	        $msg .= '<style>.SCABISH{font-family: microsoft yahei,Monaco,Consolas;font-size:100%;margin:.2em;padding:.6em;clear:both;text-align:left;background:#f6f5ec;color:#000;}';
	        $msg .= '.SCABISH p{margin:0;padding:0;line-height:1.2em;}';
	        $msg .= '.SCABISH .trace{border-top:1px dashed silver;padding-top:5px;margin-top:5px;}';
	        $msg .= '.SCABISH .homepage{text-align:right;}';
	        $msg .= '.SCABISH .homepage a{font-size:.8em;color:#7bbfea;text-decoration:none;}</style>';
	        $msg .= '<div class="SCABISH">';
	        $msg .= '<p>异常代码：'.self::translate($data['code']).'</p>';
	        $msg .= '<p>异常信息：<span style="color:#f15a22;">'.$data['message'].'</span></p>';
	        $msg .= '<p>触发位置：'.$data['file'].'('.$data['line'].')</p>';
	        $msg .= $data['trace'] ? '<div class="trace"><p>跟踪信息：</p>'.nl2br($data['trace']).'</div>' : '';
	        $msg .= '<p class="homepage"><a href="http://git.oschina.net/pycorvn/scabish" target="blank">@Scabish Framework</a></p>';
	        $msg .= '</div>';
	        if($stop) {
	            header("HTTP/1.1 500 Internal Server Error");
	            die($msg);
	        } elseif(SC_DEBUG) {
	            echo $msg;
	        }
    	} else {
    		if(in_array($data['code'], array(E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE))) return;
    		if(SCS::config()->error) {
    			SCS::request()->route(SCS::config()->error);
    		} else {
    			header("HTTP/1.1 500 Internal Server Error");
    			die($data['message']);
    		}
    	}
    }
    
    /**
     * 记录异常信息到日志
     * @param array $log
     */
    protected static function log(array $log) {
        if(false !== stripos($log['code'], 'is deprecated in PHP')) return;
        $log_path = rtrim(SCConfig::get('LOG_PATH'), '/');
        $log_path || $log_path = SC_LOG_PATH;
        $target_path = $log_path;
        mkdir($target_path, 0775, true);
        $target_file = $log_path.'/'.date('Ym').'.log';
        if(file_exists($target_file) && is_file($target_file)) {
        } else {
            touch($target_file);
        }
        
        $msg = '['.date('Y-m-d H:i:s').']'.PHP_EOL;
        $msg .= '异常代码: '.self::translate($log['code']).PHP_EOL;
        $msg .= '异常信息: '.$log['message'].PHP_EOL;
        $msg .= '发生文件: '.$log['file'].'('.$log['line'].')'.PHP_EOL;
        $msg .= '跟踪信息: '.PHP_EOL.$log['trace'].PHP_EOL;
        $msg .= str_pad('', 100, '-').PHP_EOL;
        
        file_put_contents($target_file, $msg.file_get_contents($target_file));
    }
    
    /**
     * 错误代码翻译
     * @param integer $code
     * @return string
     */
    protected static function translate($code) {
        $map = array(
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            0 => 'E_USER_ERROR',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
        );
        return isset($map[$code]) ? $map[$code] : $code;
    }
    
    /**
     * 获取debug跟踪信息
     */
    protected static function getDebugTraceString() {
        $traces = debug_backtrace();
        $trace = $traces[1];
        if($trace['function'] == 'exceptionHandler') {
            $args = $trace['args'][0];
            $list = $args->getTrace();
            foreach($list as $k=>$v) {
                $t = isset($v['class']) ? $v['class'].':' : '';
                $t .= isset($v['function']) ? $v['function'].'()' : '';
                $t .= isset($v['file']) ? ' ['.$v['file'].', '.$v['line'].']' : '';
                $list[$k] = $t; 
            }
            return implode(PHP_EOL, $list);
        } elseif($trace['function'] == 'errorHandler') {
            ob_start();
            debug_print_backtrace();
            $traces = explode(PHP_EOL, htmlentities(ob_get_clean()));
            foreach($traces as $k=>$trace) {
                preg_match('/^#(\d+)  /', $trace, $matches);
                if(!isset($matches[1])) {
                    unset($traces[$k]);
                } else {
                    if(strlen($traces[$k]) > 200) {
                        $traces[$k] = substr($traces[$k], 0, 200).' ...';
                    }
                }
            }
            return implode(PHP_EOL, $traces);
        } elseif($trace['function'] == 'fatalHandler') {
            return '';
        }
    }
}